#include "stdafx.h"

#include "parser_internal.h"
#include "parser_state.h"
#include "parser_errors.h"

#include "ast_declarations.h"
#include "ast_meta.h"
#include "ast_statements.h"

#include <lean/functional/algorithm.h>

namespace funpl
{

namespace parser
{

bool is_overloadable_operator(token_type::t type)
{
	using namespace token_type;

	switch (type)
	{
	case op_period:
	
	case op_mul:
	case op_div:
	case op_mod:

	case op_add:
	case op_sub:

	case op_less:
	case op_less_equal:
	case op_greater:
	case op_greater_equal:

	case op_equal:
	case op_not_equal:

	case op_and:
	case op_xor:
	case op_or:

	case op_to:
	
	case op_assign:

	case op_add_assign:
	case op_sub_assign:
	case op_mul_assign:
	case op_div_assign:
	case op_mod_assign:
	
	case op_and_assign:
	case op_xor_assign:
	case op_or_assign:

	case op_not:
	
	case op_increment:
	case op_decrement:
		return true;

	default:
		return false;
	}
}

/// Parses a list of meta-parameters.
ast::metaparameters* parse_metaparameters(parser_state &state, ast::node *parent)
{
	expect_cont(state, token_type::op_less);

	auto params = state.tree->create_node<ast::metaparameters>(parent);

	// Explicit template parameters
	do
	{
		// Metavariable
		if (check_cont(state, token_type::kw_def))
		{
			auto variable = parse_metavariable(state, params, false);
			params->parameters.push_back( ast::metaparameters::parameter(variable->name, variable) );
		}
		// Metatype
		else
		{
			auto type = parse_metatype(state, params, false);
			params->parameters.push_back( ast::metaparameters::parameter(type->name, type) );
		}
	}
	while (accept_cont(state, token_type::tok_comma));

	expect_cont(state, token_type::op_greater);

	return params;
}

/// Parses a function declaration.
ast::function_declaration* parse_function_declaration(parser_state &state, ast::node *parent, bool anonymous, bool implicitSemicolon)
{
	expect_cont(state, token_type::kw_fun);

	auto function = state.tree->create_node<ast::function_declaration>(parent);

	// Reference function
	if (accept_cont(state, token_type::op_and))
		function->functionClass = ast::function_class::reference;
	// Placement function
	else if (accept_cont(state, token_type::op_add))
		function->functionClass = ast::function_class::constructor;
	// Destructor
	else if (accept_cont(state, token_type::op_tilde))
		function->functionClass = ast::function_class::destructor;

	if (!anonymous)
	{
		// Context-sensitive operator keyword
		if (check_cont(state, token_type::tok_identifier) && lean::equal(state.token.range, lean::make_char_range("operator")))
		{
			accept(state);
		
			// Make sure operator is overloadable
			if (!is_overloadable_operator(next_token_cont(state).token.type))
				FUNPL_PARSE_ERROR(
						state, debug::error_code::unexpected_token,
						"unexpeced token \"" << lean::from_range<std::string>(state.token.range)
							<< "\" (" << token_type_name(state.token.type)
							<< "), expected overloadable operator"
					);

			// Operator name
			accept(state);
			function->name = state.token.range;
		}
		else
			// Function name
			function->name = expect_cont(state, token_type::tok_identifier).token.range;
	}

	// Explicit template parameters
	if (check_cont(state, token_type::op_less))
		function->metaparameters = parse_metaparameters(state, function);

	// Parameters
	expect_cont(state, token_type::tok_oparen);

	// Allow for 0 parameters
	if (!check_cont(state, token_type::tok_cparen))
		do
		{
			auto variable = parse_variable(state, function, false);
			function->parameters.push_back( ast::function_declaration::parameter(variable->name, variable) );
		}
		while (accept_cont(state, token_type::tok_comma));

	expect_cont(state, token_type::tok_cparen);

	// Context-sensitive modifiers
	while (check_cont(state, token_type::tok_identifier))
	{
		// Put stack on heap
		if (lean::equal(state.token.range, lean::make_char_range("stack_on_heap")))
			function->stackOnHeap = true;
		// Unknown modifier, break & fail
		else
			break;

		// Valid modifier
		accept(state);
	}

	// Return type
	if (accept_cont(state, token_type::op_to))
		function->returnType = parse_type(state, function, implicitSemicolon);

	return function;
}

/// Parses a function.
ast::function* parse_function(parser_state &state, ast::node *parent, bool anonymous, bool implicitSemicolon)
{
	auto function = state.tree->create_node<ast::function>(parent);

	// Declaration
	function->decl = parse_function_declaration(state, function, anonymous, implicitSemicolon);

	// Functional notation
	if (accept_cont(state, token_type::op_assign))
	{
		// NOTE: Global scope, no implicit semicolons necessary (keywords!)
		function->body = parse_expression(state, function, implicitSemicolon);
		function->autoReturn = true;
	}
	// Sequential notation
	else
		function->body = parse_compound(state, function);

	return function;
}

/// Parses a full function.
ast::function* parse_global_function(parser_state &state, ast::node *parent)
{
	return parse_function(state, parent, false, false);
}

/// Parses a structure.
ast::struct_* parse_struct(parser_state &state, ast::node *parent)
{
	expect_cont(state, token_type::kw_struct);
	
	auto struct_ = state.tree->create_node<ast::struct_>(parent);

	// Structure name
	struct_->name = expect_cont(state, token_type::tok_identifier).token.range;

	// Explicit template parameters
	if (check_cont(state, token_type::op_less))
		struct_->metaparameters = parse_metaparameters(state, struct_);

	// Specialization
	if (accept_cont(state, token_type::op_on))
		struct_->specialization = parse_metaarguments(state, struct_, false);

	expect_cont(state, token_type::tok_obrace);

	// Struct body
	while (!accept_is(state, token_type::tok_cbrace))
	{
		try
		{
			if (check(state, token_type::kw_typedef))
			{
				// Typedef
				auto typedef_ = parse_typedef(state, struct_, true);
				struct_->elements.push_back( ast::struct_::element(typedef_->name, typedef_) );
			}
			else
			{
				// Variable
				auto variable = parse_variable(state, struct_, true);
				struct_->elements.push_back( ast::struct_::element(variable->name, variable) );
			}

			// Make sure full variable has been parsed
			expect(state, token_type::tok_semicolon);
		}
		catch(const parse_exception&) { skip_error(state); } // TODO: skip till semicolon instead?
	}

	return struct_;
}

/// Parses a class.
ast::class_* parse_class(parser_state &state, ast::node *parent)
{
	expect_cont(state, token_type::kw_class);

	auto class_ = state.tree->create_node<ast::class_>(parent);

	// Name
	class_->name = expect_cont(state, token_type::tok_identifier).token.range;

	// Explicit template parameters
	if (check_cont(state, token_type::op_less))
		class_->metaparameters = parse_metaparameters(state, class_);

	// Specialization
	if (accept_cont(state, token_type::op_on))
		class_->specialization = parse_metaarguments(state, class_, false);

	expect_cont(state, token_type::tok_obrace);

	// Class body
	while (!accept_is(state, token_type::tok_cbrace))
	{
		try
		{
			if (check(state, token_type::kw_typedef))
			{
				// Typedef
				auto typedef_ = parse_typedef(state, class_, false);
				class_->elements.push_back( ast::class_::element(typedef_->name, typedef_) );
			}
			else
			{
				// Function
				auto function = parse_function_declaration(state, class_, false, false);
				class_->elements.push_back( ast::class_::element(function->name, function) );
			}

			// Make sure full variable has been parsed
			expect(state, token_type::tok_semicolon);
		}
		catch(const parse_exception&) { skip_error(state); } // TODO: skip till semicolon instead?
	}

	return class_;
}

/// Parses a typegroup.
ast::typegroup* parse_typegroup(parser_state &state, ast::node *parent)
{
	expect_cont(state, token_type::kw_typegroup);

	auto typegroup = state.tree->create_node<ast::typegroup>(parent);

	// Name
	typegroup->name = expect_cont(state, token_type::tok_identifier).token.range;

	expect_cont(state, token_type::tok_obrace);

	// Allow for 0 elements
	if (!check_cont(state, token_type::tok_cbrace))
		do
		{
			// Member type
			auto type = parse_type(state, typegroup, false);
			typegroup->elements.push_back(type);
		}
		while (accept_cont(state, token_type::tok_comma));

	expect_cont(state, token_type::tok_cbrace);

	return typegroup;
}

/// Parses a typedef.
ast::typedef_* parse_typedef(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	expect_cont(state, token_type::kw_typedef);

	auto typedef_ = state.tree->create_node<ast::typedef_>(parent);

	// Name
	typedef_->name = expect_cont(state, token_type::tok_identifier).token.range;

	// Explicit template parameters
	if (check_cont(state, token_type::op_less))
		typedef_->metaparameters = parse_metaparameters(state, typedef_);

	// Specialization
	if (accept_cont(state, token_type::op_on))
		typedef_->specialization = parse_metaarguments(state, typedef_, false);

	expect(state, token_type::op_assign);

	// Type
	typedef_->alias = parse_type(state, typedef_, implicitSemicolon);

	return typedef_;
}

} // namespace

} // namespace